﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Awesom_O_Controller
{
    public class AwesomoControllerClient : AoSocket
    {
        public enum ControllerEventPackets : byte
        {
            ConnectionSuccessful = 0x01,
            AlreadyConnected = 0x02,
            BnetAuthResponse = 0x03,
            BnetLogonResponse = 0x04,
            TemporaryIpBan = 0x05,
            CharacterSelect = 0x06,
            JoinChannel = 0x07,
            LogonSuccess = 0x08,
            JoinGameSuccessful = 0x09,
            ConnectedGameProxy = 0x0a,
            GameAlreadyExists = 0x0b,
            GameDoesNotExists = 0x0c,
            FailedToJoin = 0x0d,
            Message = 0x0e,
            ExitGame = 0x0f,
            ControllerMessage = 0x10,
            Variable = 0x11,
            ChatEvent = 0x12,
            ChatDestructor = 0x13,
            RealmDestructor = 0x14,
            GameDestructor = 0x15,
            CantConnect = 0x16,
            CharCount = 0x17,
            SetControllerVariable = 0x18,
            RequestControllerVariable = 0x19,

            //script packets
            ItemMessage = 0x64,
            StatusMessage = 0x65,
            ChickenExit = 0x66,
            DiaClone = 0x67,
            HotIp = 0x68,
            BeginMule = 0x69,
            MuleIsFull = 0x70,
            FinishedMule = 0x71,
            JoinMuleGame = 0x73,
            Death = 0x74,
            GameExp = 0x75,

            //AoSocket.cs socket error
            SocketError = 200
        }

        public event AwesomoControllerClientHandler PacketReceived;

        public AwesomoControllerClient()
        {
        }

        public AwesomoControllerClient(string ip)
            : base(ip)
        {
        }

        public int CreateNewControllerClient(string ownername)
        {
            if (Connect())
            {
                var buffer = new List<byte> {0x01};
                buffer.AddRange(Encoding.ASCII.GetBytes(ownername));
                buffer.Add((byte)'\0');// end the string
                return Send(buffer.ToArray(), buffer.Count);
            }
            return 0;
        }

        public bool Start()
        {
            if (IsClientConnected)
                return StartListener();
            return false;
        }

        public bool Stop()
        {
            return Disconnect();
        }

        public int StartRun()
        {
            var buffer = new List<byte> {0x02};
            return Send(buffer.ToArray(), buffer.Count);
        }

        public int StartMule()
        {
            var buffer = new byte[2];
            buffer[0] = 0x10;
            buffer[1] = (byte)'\0';
            int length = buffer.Length;
            return Send(buffer, length);
        }

        public int StartMakeMule()
        {
            var buffer = new byte[2];
            buffer[0] = 0x11;
            buffer[1] = (byte)'\0';
            int length = buffer.Length;
            return Send(buffer, length);
        }

        public int StartLeech()
        {
            var buffer = new byte[2];
            buffer[0] = 0x13;
            buffer[1] = (byte)'\0';
            int length = buffer.Length;
            return Send(buffer, length);
        }

        public int SendMsgToConClients(string msg)
        {
            var buffer = new List<byte> {0x03};
            buffer.AddRange(Encoding.ASCII.GetBytes(msg));
            buffer.Add((byte)'\0');
            return Send(buffer.ToArray(), buffer.Count);
        }

        public int SetCoreVar(string varname, string value)
        {
            var buffer = new List<byte> {0x04};
            buffer.AddRange(Encoding.ASCII.GetBytes(varname));
            buffer.Add((byte)'\0');
            buffer.AddRange(Encoding.ASCII.GetBytes(value));
            buffer.Add((byte)'\0');
            return Send(buffer.ToArray(), buffer.Count);
        }

        public int RunLuaFile(string filename, string function)
        {
            var buffer = new List<byte> {0x05};
            buffer.AddRange(Encoding.ASCII.GetBytes(filename));
            buffer.Add((byte)'\0');
            buffer.AddRange(Encoding.ASCII.GetBytes(function));
            buffer.Add((byte)'\0');
            return Send(buffer.ToArray(), buffer.Count);
        }

        public int LeaveGame()
        {
            var buffer = new List<byte> {0x06, (byte) '\0'};
            return Send(buffer.ToArray(), buffer.Count);
        }

        public int AddCdkey(string key, string gamever)
        {
            var buffer = new List<byte> {0x08};
            buffer.AddRange(Encoding.ASCII.GetBytes(key));
            buffer.Add((byte)'\0');
            buffer.AddRange(Encoding.ASCII.GetBytes(gamever));
            buffer.Add((byte)'\0');
            return Send(buffer.ToArray(), buffer.Count);
        }

        public int ClearCdkeys()
        {
            var buffer = new byte[] { 0x09 };
            return Send(buffer, buffer.Length);
        }
        public int ReleaseCdkeys()
        {
            var buffer = new byte[] { 0x12 };
            return Send(buffer, buffer.Length);
        }

        protected override void PacketHandler(byte[] packets)
        {
            Console.WriteLine(((ControllerEventPackets)packets[0]).ToString());

            switch ((ControllerEventPackets)packets[0])
            {
                case ControllerEventPackets.ConnectionSuccessful:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ConnectionSuccessful));
                    break;
                case ControllerEventPackets.AlreadyConnected:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.AlreadyConnected));
                    break;
                case ControllerEventPackets.BnetAuthResponse:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.BnetAuthResponse, packets[1]));
                    break;
                case ControllerEventPackets.BnetLogonResponse:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.BnetLogonResponse, packets[1]));
                    break;
                case ControllerEventPackets.TemporaryIpBan:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.TemporaryIpBan));
                    break;
                case ControllerEventPackets.CharacterSelect:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CharacterSelect));
                    break;
                case ControllerEventPackets.JoinChannel:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.JoinChannel));
                    break;
                case ControllerEventPackets.LogonSuccess:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.LogonSuccess));
                    break;
                case ControllerEventPackets.JoinGameSuccessful:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.JoinGameSuccessful));
                    break;
                case ControllerEventPackets.ConnectedGameProxy:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ConnectedGameProxy));
                    break;
                case ControllerEventPackets.GameAlreadyExists:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.GameAlreadyExists));
                    break;
                case ControllerEventPackets.GameDoesNotExists:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.GameDoesNotExists));
                    break;
                case ControllerEventPackets.FailedToJoin:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.FailedToJoin));
                    break;
                case ControllerEventPackets.Message:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.Message, BitConverter.ToString(packets, 1)));
                    break;
                case ControllerEventPackets.ExitGame:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ExitGame));
                    break;
                case ControllerEventPackets.ControllerMessage:
                    break;
                case ControllerEventPackets.Variable:
                    break;
                case ControllerEventPackets.ChatEvent:
                    ChatMessage cmsg = ChatMessageParser.ParseChatMessage(packets);
                    //Logging.WriteInfo(cmsg.Account + " " + cmsg.CharName + " " + cmsg.Message);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ChatEvent, cmsg.Account, cmsg.CharName, cmsg.Message));
                    break;
                case ControllerEventPackets.CantConnect:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CantConnect));
                    break;
                case ControllerEventPackets.ItemMessage:
                    string msg = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    //Logging.WriteInfo("Item: " + msg);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ItemMessage, msg));
                    break;
                case ControllerEventPackets.StatusMessage:
                    string status = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    //Logging.WriteInfo("Status: " + status);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.StatusMessage, status));
                    break;
                case ControllerEventPackets.ChickenExit:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ChickenExit));
                    break;
                case ControllerEventPackets.DiaClone:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.DiaClone, Encoding.ASCII.GetString(packets, 1, packets.Length - 1)));
                    break;
                case ControllerEventPackets.HotIp:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.HotIp, Encoding.ASCII.GetString(packets, 1, packets.Length - 1)));
                    break;
                case ControllerEventPackets.BeginMule:
                    string beginMule = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    //Logging.WriteInfo("Muling started.");
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.BeginMule, beginMule));
                    break;
                case ControllerEventPackets.MuleIsFull:
                    string muleIsFull = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    //Logging.WriteInfo("Mule is full, changing char...");
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.MuleIsFull, muleIsFull));
                    break;
                case ControllerEventPackets.FinishedMule:
                    string finishedMule = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    //Logging.WriteInfo("Muling finished, returning to normal botting...");
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.FinishedMule, finishedMule));
                    break;
                case ControllerEventPackets.JoinMuleGame:
                    string joinMule = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    //Logging.WriteInfo("Muling is started, joining game...");
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.JoinMuleGame, joinMule));
                    break;
                case ControllerEventPackets.Death:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.Death));
                    break;
                case ControllerEventPackets.CharCount:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CharCount, packets[1]));
                    //Logging.WriteInfo("Found " + packets[1].ToString() + " characters");
                    break;
                case ControllerEventPackets.GameExp:
                    string gameExperience = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.GameExp, gameExperience));
                    break;
                case ControllerEventPackets.SetControllerVariable:
                    ControllerVariable variable = ControllerVariableParser.ParseControllerVariable(packets);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.SetControllerVariable, variable.Name + "|" + variable.Value));
                    break;
            }
        }

        protected override void SocketErrorHandler(string errmsg)
        {
            Log.Error("Controller socket error: " + errmsg, "Core");
            PacketReceived(this, new PacketArgs(ControllerEventPackets.SocketError, errmsg));
        }
    }

    #region Controller event

    public delegate void AwesomoControllerClientHandler(object sender, PacketArgs myArgs);

    public class PacketArgs : EventArgs
    {
        readonly AwesomoControllerClient.ControllerEventPackets _event;
        readonly string _account;
        readonly string _charName;
        readonly string _message;
        readonly int _result;

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets @event)
            : this(@event, "", 0)
        {
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets @event, string message)
            : this(@event, message, 0)
        {
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets @event, int result)
            : this(@event, "", result)
        {
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets @event, string message, int result)
        {
            _event = @event;
            _message = message;
            _result = result;
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets @event, string account, string charName, string message)
        {
            _event = @event;
            _account = account;
            _charName = charName;
            _message = message;
        }

        public AwesomoControllerClient.ControllerEventPackets Event
        {
            get { return _event; }
        }

        public string Account
        {
            get { return _account; }
        }

        public string CharName
        {
            get { return _charName; }
        }

        public string Message
        {
            get { return _message; }
        }

        public int Result
        {
            get { return _result; }
        }
    }

    #endregion

    public static class ChatMessageParser
    {
        public static ChatMessage ParseChatMessage(byte[] buffer)
        {
            int offset = 1;

            while (buffer[offset] != 0)
            {
                offset++;
            }

            int length = offset - 1;

            string acc = Encoding.ASCII.GetString(buffer, 1, length);
            length = 0;
            offset++;

            while (buffer[offset] != 0)
            {
                offset++;
                length++;
            }

            string charname = Encoding.ASCII.GetString(buffer, acc.Length + 2, length);
            length = 0;
            offset++;

            while (buffer[offset] != 0)
            {
                offset++;
                length++;
            }

            string message = Encoding.ASCII.GetString(buffer, acc.Length + charname.Length + 3, length);

            return new ChatMessage(acc, charname, message);
        }
    }

    public class ChatMessage
    {
        public readonly string Account;
        public readonly string CharName;
        public readonly string Message;

        public ChatMessage(string account, string charName, string message)
        {
            Account = account;
            CharName = charName;
            Message = message;
        }
    }

    public static class ControllerVariableParser
    {
        public static ControllerVariable ParseControllerVariable(byte[] buffer)
        {
            int offset = 1;

            while (buffer[offset] != 0)
            {
                offset++;
            }

            int length = offset - 1;

            string name = Encoding.ASCII.GetString(buffer, 1, length);
            length = 0;
            offset++;

            while (buffer[offset] != 0)
            {
                offset++;
                length++;
            }

            string value = Encoding.ASCII.GetString(buffer, name.Length + 2, length);

            return new ControllerVariable(name, value);
        }
    }

    public class ControllerVariable
    {
        public readonly string Name, Value;

        public ControllerVariable(string name, string value)
        {
            Name = name;
            Value = value;
        }
    }
}
